package com.lazi.june.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.lazi.june.common.base.RequestConst;
import com.lazi.june.gateway.config.GatewayProperties;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.SignedJWT;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.Set;


/**
 * JWT 认证过滤器
 * <p>
 * 功能说明：
 * 1. 白名单路径直接放行（如 /actuator/**、/public/**）
 * 2. 内部路径需验证 X-Internal-Secret 请求头（防止外网直接访问内部接口）
 * 3. 其他路径需验证 JWT Token（Bearer 格式）
 * <p>
 * 支持的 JWT 算法：
 * - HMAC: HS256、HS512（使用共享密钥，june.gateway.jwt-public-key 配置为字符串）
 * - RSA: RS256、RS512（使用 JWK 格式公钥，june.gateway.jwt-public-key 配置为 JWK JSON）
 * <p>
 * 配置示例：
 * <pre>
 * june:
 *   gateway:
 *     whitelist:
 *       - /actuator/**
 *       - /public/**
 *     internal-paths:
 *       - /internal/**
 *     internal-secret: myInternalSecret123
 *     jwt-public-key: ${JWT_PUBLIC_KEY}  # HMAC 密钥或 JWK JSON
 * </pre>
 *
 * @author huliqian
 * @since 2025/1/15
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthFilter implements GlobalFilter, Ordered {

    private final GatewayProperties props;
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    /**
     * 过滤器核心逻辑
     * <p>
     * 执行顺序：
     * 1. 检查是否在白名单中 -> 放行
     * 2. 检查是否为内部路径 -> 验证 X-Internal-Secret
     * 3. 验证 JWT Token（Authorization: Bearer xxx）
     *
     * @param exchange 当前请求的 ServerWebExchange
     * @param chain    过滤器链
     * @return Mono<Void> 异步响应
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();

        // 获取认证配置
        GatewayProperties.AuthConfig authConfig = props.getAuth();

        // 1. 白名单路径直接放行
        if (matchAny(path, authConfig.getWhitelist())) {
            return chain.filter(exchange);
        }

        // 2. 内部路径验证（需要携带正确的 X-Internal-Secret）
        if (matchAny(path, authConfig.getInternalPaths())) {
            String secret = exchange.getRequest().getHeaders().getFirst(RequestConst.INTERNAL_SECRET);
            if (!StrUtil.equals(secret, authConfig.getInternalSecret())) {
                log.warn("[JWT-Auth] Internal path access denied: {} (invalid secret)", path);
                exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        }

        // 3. JWT Token 验证
        String auth = exchange.getRequest().getHeaders().getFirst(RequestConst.TOKEN);
        if (StrUtil.isBlank(auth) || !StrUtil.startWithIgnoreCase(auth, "Bearer ")) {
            log.warn("[JWT-Auth] Missing or invalid Authorization header: {}", path);
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        String token = StrUtil.subAfter(auth, " ", false);
        try {
            if (!verify(token)) {
                log.warn("[JWT-Auth] JWT verification failed: {}", path);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
        } catch (Exception e) {
            log.warn("[JWT-Auth] JWT verification exception for {}: {}", path, e.getMessage(), e);
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 检查路径是否匹配任一 Ant 风格模式
     *
     * @param path     请求路径
     * @param patterns Ant 风格路径模式集合（如 /api/**、/internal/*）
     * @return 是否匹配
     */
    private boolean matchAny(String path, Set<String> patterns) {
        return patterns.stream().anyMatch(pattern -> pathMatcher.match(pattern, path));
    }

    /**
     * 验证 JWT Token
     * <p>
     * 支持算法：
     * - HS256/HS512: HMAC 签名，使用 jwt-public-key 作为共享密钥
     * - RS256/RS512: RSA 签名，jwt-public-key 需为 JWK JSON 格式
     *
     * @param token JWT 字符串
     * @return 验证是否通过
     * @throws Exception 解析或验证异常
     */
    private boolean verify(String token) throws Exception {
        SignedJWT jwt = SignedJWT.parse(token);
        JWSAlgorithm alg = jwt.getHeader().getAlgorithm();
        String key = props.getJwt().getPublicKey();
        if (key == null) return false;

        // RSA 算法验证（需要 JWK 公钥）
        if (JWSAlgorithm.RS256.equals(alg) || JWSAlgorithm.RS512.equals(alg)) {
            try {
                JWK jwk = JWK.parse(key);
                RSAKey rsaKey = jwk.toRSAKey();
                RSASSAVerifier verifier = new RSASSAVerifier(rsaKey);
                return jwt.verify(verifier);
            } catch (ParseException ignore) {
                log.warn("[JWT-Auth] Invalid JWK format for RSA verification");
                return false;
            }
        }

        // HMAC 算法验证（使用共享密钥）
        if (JWSAlgorithm.HS256.equals(alg) || JWSAlgorithm.HS512.equals(alg)) {
            MACVerifier verifier = new MACVerifier(key.getBytes(StandardCharsets.UTF_8));
            return jwt.verify(verifier);
        }

        log.warn("[JWT-Auth] Unsupported JWT algorithm: {}", alg);
        return false;
    }

    /**
     * 过滤器执行顺序
     * <p>
     * 执行顺序参考：
     * - CorsFilter: -200 (CORS 跨域)
     * - BlacklistFilter: -110 (黑名单检查)
     * - JwtAuthFilter: -100 (认证)
     * - RequestBodyCacheFilter: -90 (请求体缓存)
     * - RateLimitFilter: -88 (限流)
     * - RequestLoggingFilter: -80 (日志)
     *
     * @return 顺序值，越小越早执行，-100 表示在大多数过滤器之前执行
     */
    @Override
    public int getOrder() {
        // 优先执行，确保鉴权在其他业务逻辑之前
        return -100;
    }
}